package com.xiaoq.matrix.minipro.endpoint;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.xiaoq.RedisTemplate4JedisCluster;
import com.xiaoq.coms.util.ConstantUtil;
import com.xiaoq.coms.util.JsonUtil;
import com.xiaoq.matrix.minipro.entity.ShopMiniProEntity;
import com.xiaoq.matrix.minipro.repository.impl.ShopMiniProRepositoryImpl;
import com.xiaoq.matrix.minipro.service.impl.MiniProServiceImpl;
import com.xiaoq.matrix.minipro.service.impl.WxOpenServiceDemo;
import com.xiaoq.matrix.minipro.util.WxMpXmlMessage;
import com.xiaoq.matrix.minipro.util.WxOpenXmlMessage;
import com.xiaoq.matrix.minipro.vo.MiniProAuthorizerInfo;
import com.xiaoq.matrix.minipro.vo.MiniProBaseResponse;
import com.xiaoq.matrix.minipro.vo.MiniProDraftOrTemplate;
import com.xiaoq.matrix.minipro.vo.MiniProDraftOrTemplateListResponse;
import com.xiaoq.matrix.weixin.WXToken;

import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;

@RestController
@RequestMapping("/api/v1/weixin/minip")
public class MiniProEndpoint {
	private static final Logger LOG = LoggerFactory.getLogger(MiniProEndpoint.class);
	
	@Autowired
	private  MiniProServiceImpl miniProService;
	
	@Autowired
    protected WxOpenServiceDemo wxOpenService;
	
	@Autowired
	private ShopMiniProRepositoryImpl shopMiniPro;
	
	@Autowired
	private RedisTemplate4JedisCluster redisTemplate;
	
	/**
	 * 公众号回调接口
	 * @param userId
	 * @param authCode
	 * @param expiresIn
	 * @param request
	 * @param response
	 */
	@ApiOperation(value = "响应微信服务器授权回调，获取到auth_code", notes = "响应微信服务器授权回调，获取到auth_code")
	@GetMapping(value = "/authCode")
	public void callback(
			@RequestParam(value="userId",required = false) String userId,
			@RequestParam(value = "auth_code", required = false) String authCode,
			@RequestParam(value = "expires_in", required = false) Long expiresIn,
			HttpServletRequest request,HttpServletResponse response) {
		LOG.info("公众平台的认证userId"+userId+"公众类型 公众号");
		LOG.info("微信认证回调接口：auth_code:"+authCode+"---expiresIn:"+expiresIn);
		MiniProAuthorizerInfo info = miniProService.authCodeCallback(userId, authCode,"1");
		String out = null;
		if(info==null){
			out = "http://qiniumatrix.xiaoq.online/fail1.html";
			
		}else{
			out = "http://qiniumatrix.xiaoq.online/success1.html";
		}
		
		try {
			response.sendRedirect(out);
		} catch (IOException e) {
			LOG.error(e.getMessage(),e);
		}
	}
	
	@ApiOperation(value = "响应小程序回调接口，获取到auth_code", notes = "响应小程序回调接口，获取到auth_code")
	@GetMapping(value = "/mini/authCodes")
	public void miniAuth(
			@RequestParam(value="userId",required = false) String userId,
			@RequestParam(value = "auth_code", required = false) String authCode,
			@RequestParam(value = "expires_in", required = false) Long expiresIn,
			HttpServletRequest request,HttpServletResponse response) {
		LOG.info("公众平台的认证userId"+userId+"公众类型 小程序");
		LOG.info("微信小程序回调认证回调接口：auth_code:"+authCode+"---expiresIn:"+expiresIn);
		String out = "http://p0sd9v5tn.bkt.clouddn.com/success.html";
		String shopId = redisTemplate.get(ConstantUtil.REDIS_PREFIX_MANAGER_SHOP_RELATION, userId);
		MiniProAuthorizerInfo miniProAuthorizer = miniProService.authCodeCallback(userId, authCode,"2");
		String appId = miniProAuthorizer.getAuthorizerAppId();
		LOG.info("shopId"+shopId+"appId"+appId);
		if(StringUtils.isBlank(shopId)||StringUtils.isBlank(appId)){
			String fail = "http://p0sd9v5tn.bkt.clouddn.com/fail.html";
			try {
				response.sendRedirect(fail);
			} catch (IOException e) {
				LOG.error(e.getMessage(),e);
			}
		}
		String accessToken = miniProAuthorizer.getAuthorizerAccessToken();
		// 这会不能平凡的给小程序添加服务器
//		MiniProBaseResponse miniPro = miniProService.saveDomainConf2Weixin(accessToken);
//		LOG.info("给小程序重新添加服务器地址结果"+miniPro);
		MiniProDraftOrTemplateListResponse miniProTemplate = miniProService.getTemplateListFromWeixin();
		LOG.info("获取版本版本库集合的结果为"+ miniProTemplate);
		List<MiniProDraftOrTemplate> miniProTemplateList = miniProTemplate.getTemplateList();
		LOG.info("获取版本版本库集合的数据为"+miniProTemplateList);
		MiniProDraftOrTemplate miniTem = miniProTemplateList.get(miniProTemplateList.size()-1);
		Long draftId = miniTem.getTemplateId();
		LOG.info("添加的版本Id为"+draftId);
		String version = miniTem.getUserVersion();
		String desc = miniTem.getUserDesc();
//		String extJson = "{\r\n" + 
//				"  \"extEnable\": true,\r\n" + 
//				"  \"extAppid\": \"wx45d9b5b007c2c7c7\",\r\n" + 
//				"  \"directCommit\": false,\r\n" + 
//				"  \"networkTimeout\": {\r\n" + 
//				"    \"request\": 10000,\r\n" + 
//				"    \"connectSocket\": 10000,\r\n" + 
//				"    \"uploadFile\": 10000,\r\n" + 
//				"    \"downloadFile\": 10000\r\n" + 
//				"  }\r\n" + 
//				"}";
		String extJson = "{\r\n" + 
				"  \"extEnable\": true,\r\n" + 
				"  \"extAppid\": \""+appId+"\",\r\n" + 
				"  \"directCommit\": false,\r\n" + 
				"  \"ext\": {\r\n" + 
				"    \"sites\": \""+shopId+"\"\r\n" + 
				"  },\r\n" + 
				"  \"networkTimeout\": {\r\n" + 
				"    \"request\": 10000,\r\n" + 
				"    \"connectSocket\": 10000,\r\n" + 
				"    \"uploadFile\": 10000,\r\n" + 
				"    \"downloadFile\": 10000\r\n" + 
				"  }\r\n" + 
				"}";
		
		LOG.info(extJson);
		MiniProBaseResponse miniPro2 = miniProService.commitCode2Weixin(accessToken, draftId, extJson, version, desc);
		LOG.info("给小程序添加代码的结果"+miniPro2);
		try {
			response.sendRedirect(out);
		} catch (IOException e) {
			LOG.error(e.getMessage(),e);
		}
	}
	
	@ApiOperation(value = "响应微信服务器发出的ticket心跳", notes = "响应微信服务器发出的ticket心跳")
	@ApiResponses({ @ApiResponse(code = 200, response = WXToken.class, message = "token") })
	@PostMapping(value = "/ticket")
	public Object receiveTicket(@RequestBody(required = false) String requestBody,
			@RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce,
			@RequestParam("signature") String signature,
			@RequestParam(name = "encrypt_type", required = false) String encType,
			@RequestParam(name = "msg_signature", required = false) String msgSignature) {
		LOG.info(
				"\n接收微信请求：[signature=[{}], encType=[{}], msgSignature=[{}],"
						+ " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
				signature, encType, msgSignature, timestamp, nonce, requestBody);

		// TODO 这里可能有一个伪造请求没有处理
		// if (!StringUtils.equalsIgnoreCase("aes", encType)
		// ||
		// !wxOpenService.getWxOpenComponentService().checkSignature(timestamp,
		// nonce, signature)) {
		// throw new IllegalArgumentException("非法请求，可能属于伪造的请求！");
		// }

		// aes加密的消息
		WxOpenXmlMessage inMessage = WxOpenXmlMessage.fromEncryptedXml(requestBody, timestamp, nonce, msgSignature);
		LOG.info("解析后的消息为："+inMessage.getComponentVerifyTicket());
		String out = "";
		try {
			miniProService.setTicket(inMessage.getComponentVerifyTicket());
			out = "success";
		} catch (Exception e) {
			LOG.error("receive_ticket", e);
		}

		return out;
	}

	// 纯粹为了让微信服务器可以确认用户录入的服务器地址是有效的，
	// 所以简单的给微信服务器回显一个字符串一个，严格上需要验证请求的签名，在此略过
	@ApiOperation(value = "响应微信服务器发出的Token验证", notes = "响应微信服务器发出的Token验证")
	@GetMapping(value = "/verify_token")
	public void verifyToken(HttpServletRequest request, HttpServletResponse resp) {
		// TODO 需要添加验证签名的逻辑
		
		Map<String, String[]> reqMap = request.getParameterMap();
		if (LOG.isDebugEnabled()) {
			LOG.debug("Get Request: " + JsonUtil.asString(reqMap));
		}
		PrintWriter w = null;
		try {
			w = resp.getWriter();
			String[] echostr = reqMap.get("echostr");
			if (echostr != null && echostr.length > 0) {
				w.print(echostr[0]);
			}
			w.flush();
		} catch (IOException e) {
			LOG.error("", e);
		} finally {
			IOUtils.closeQuietly(w);
		}
	}
	
/*	
	6、获取审核结果
	当小程序有审核结果后，第三方平台将可以通过开放平台上填写的回调地址，获得审核结果通知。
	审核通过时，接收到的推送XML数据包示例如下：
	<xml>
		<ToUserName><![CDATA[gh_fb9688c2a4b2]]></ToUserName>
		<FromUserName><![CDATA[od1P50M-fNQI5Gcq-trm4a7apsU8]]></FromUserName>
		<CreateTime>1488856741</CreateTime>
		<MsgType><![CDATA[event]]></MsgType>
		<Event><![CDATA[weapp_audit_success]]></Event>
		<SuccTime>1488856741</SuccTime>
	</xml>
	
	参数说明：
	ToUserName	小程序的原始ID
	FromUserName 	发送方帐号（一个OpenID，此时发送方是系统帐号）
	CreateTime	消息创建时间 （整型），时间戳
	MsgType	消息类型，event
	Event	事件类型 weapp_audit_success
	SuccTime	审核成功时的时间（整形），时间戳
	
	审核不通过时，接收到的推送XML数据包示例如下：
	<xml>
		<ToUserName><![CDATA[gh_fb9688c2a4b2]]></ToUserName>
		<FromUserName><![CDATA[od1P50M-fNQI5Gcq-trm4a7apsU8]]></FromUserName>
		<CreateTime>1488856591</CreateTime>
		<MsgType><![CDATA[event]]></MsgType>
		<Event><![CDATA[weapp_audit_fail]]></Event>
		<Reason><![CDATA[
			1:账号信息不符合规范:<br>
				(1):包含色情因素<br>
			2:服务类目"金融业-保险_"与你提交代码审核时设置的功能页面内容不一致:<br>
				(1):功能页面设置的部分标签不属于所选的服务类目范围。<br>
				(2):功能页面设置的部分标签与该页面内容不相关。<br>
		]]
		</Reason>
		<FailTime>1488856591</FailTime>
	</xml>
	
	参数说明：
	ToUserName	小程序的原始ID
	FromUserName 	发送方帐号（一个OpenID，此时发送方是系统帐号）
	CreateTime	消息创建时间 （整型），时间戳
	MsgType	消息类型，event
	Event	事件类型 weapp_audit_success
	Reason  	审核失败的原因
	FailTime 	审核失败时的时间（整型），时间戳
	
	除了消息通知之外，第三方平台也可通过接口查询审核状态。

*/
	// 微信服务器会将特定公众号或者特定小程序的事件通知发送到这个地址
	// 可能受到消息类型各种各样
	// 这里对应第三方平台中的【公众号消息与事件接收URL】这个配置项
//	@PostMapping(value = "/{appId}/event")
//	public void onEvent(HttpServletRequest request, HttpServletResponse resp) {
//		try {
//			request.setCharacterEncoding("UTF-8");
//			resp.setCharacterEncoding("UTF-8");
//		} catch (UnsupportedEncodingException e1) {
//			e1.printStackTrace();
//		}
//		MiniProEventMessage wxMsg = null;
//		InputStream ins = null;
//		try {
//			ins = request.getInputStream();
//			// TODO 通知过来的事件可能各种类型的有，这里只考虑审核结果事件，如果遇到其他类型事件，如何处理？
//			wxMsg = WeixinXmlUtil.fromInputStream(MiniProEventMessage.class, ins);
//		} catch (Exception e) {
//			LOG.warn("接收微信服务器事件通知失败", e);
//		} finally {
//			IOUtils.closeQuietly(ins);
//		}
//		if (wxMsg == null) {
//			return;
//		}
//		String msgType = wxMsg.getMsgType();
//		String appId = wxMsg.getToUserName();
//		String event = wxMsg.getEvent();
//		if ("event".equals(msgType)) {
//			if ("weapp_audit_success".equals(event)) {
//				LOG.info("审核成功事件");
//				// TODO
//				// 根据返回成功和失败的状态，更新数据库中的数据
//				// 如果成功了就自动发布上线？
//				miniProService.receivedAuditSuccess(appId);
//			}
//			if ("weapp_audit_fail".equals(event)) {
//				LOG.info("审核失败事件");
//				// TODO
//				// 根据返回成功和失败的状态，更新数据库中的数据
//				// 如果成功了就自动发布上线？
//				miniProService.receivedAuditFail(appId, wxMsg.getReason());
//			}
//		} else {
//			// TODO 其他类型事件
//			LOG.info("其他类型事件");
//		}
//	}
	
//	@PostMapping(value = "/{appId}/event")/$APPID$/callback
	@PostMapping(value = "/{appId}/callback")
	public Object onEvent(@RequestBody(required = false) String requestBody,
            @PathVariable("appId") String appId,
            @RequestParam("signature") String signature,
            @RequestParam("timestamp") String timestamp,
            @RequestParam("nonce") String nonce,
            @RequestParam("openid") String openid,
            @RequestParam("encrypt_type") String encType,
            @RequestParam("msg_signature") String msgSignature) {
		LOG.info("微信审核接口被调用！！！！！！！！！");
		LOG.info(
                "\n微信授权事件请求：[appId=[{}], openid=[{}], signature=[{}], encType=[{}], msgSignature=[{}],"
                        + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
                appId, openid, signature, encType, msgSignature, timestamp, nonce, requestBody);
		WxMpXmlMessage wxMpXmlMeaaage = WxOpenXmlMessage.fromEncryptedMpXml(requestBody, timestamp, nonce, msgSignature);
		String event = wxMpXmlMeaaage.getEvent();
		Long failTime = wxMpXmlMeaaage.getFailTime();
		String failReason = wxMpXmlMeaaage.getFailReason();
		ShopMiniProEntity  shopMiniProEntity  = shopMiniPro.getByAppId(appId);
		shopMiniProEntity.setEvent(event);
		shopMiniProEntity.setFailReason(failReason);
		shopMiniProEntity.setFailTime(failTime);
		shopMiniPro.saveShopMiniProEntity(shopMiniProEntity);
		String out = "success";
		return out;
	}
	
}
